#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsView>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 创建主窗口布局
    QWidget *centralWidget = new QWidget(this);
    QGridLayout *mainLayout = new QGridLayout(centralWidget);  // 使用网格布局
    setCentralWidget(centralWidget);

    // 第一行：摄像头
    //QVideoWidget *videoWidget = new QVideoWidget(this);
    //调用笔记本电脑摄像头，最初用来验证视频流处理代码
    // mainLayout->addWidget(videoWidget, 0, 0, 1, 2);  // 第 0 行，第 0 列，占 1 行 2 列

    // // 初始化摄像头
    // const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
    // if (!cameras.isEmpty()) {
    //     camera = new QCamera(cameras.first(), this);
    //     camera->setViewfinder(videoWidget);
    //     camera->start();
    //     qDebug() << "摄像头已启动";
    // } else {
    //     qWarning("未检测到摄像头设备");
    // }
    //调用深度摄像头
    // 创建布局

    // 第1行0列：普通视频显示

    // 创建视频显示区域（应该是qt的限制，编写video类，在类里处理socket和形成image,到主线程显示到ui,一定会内存溢出，所以调整为在主线程完成视频的显示）
    //这种方式的视频比通过ROS2的方式，流畅度，清晰度强很多，不卡顿
    colorVideoLabel = new QLabel(this);
    colorVideoLabel->setAlignment(Qt::AlignCenter);
    colorVideoLabel->setMinimumSize(800, 600);
    colorVideoLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    mainLayout->addWidget(colorVideoLabel, 0, 0, 1, 2);

    // 初始化 Socket
    socket = new QTcpSocket(this);
    connect(socket, &QTcpSocket::connected, this, &MainWindow::onSocketConnected);
    connect(socket, &QTcpSocket::disconnected, this, &MainWindow::onSocketDisconnected);
    connect(socket, &QTcpSocket::readyRead, this, &MainWindow::onSocketReadyRead);
    connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::errorOccurred),
            this, &MainWindow::onSocketError);

    // 连接服务器
    socket->connectToHost("192.168.0.106", 5000); // 替换为你的服务器 IP 和端口

    // 第1行1列：深度视频显示
    // QLabel *depthVideoLabel = new QLabel(this);
    // depthVideoLabel->setAlignment(Qt::AlignCenter);
    // depthVideoLabel->setStyleSheet("background-color: black;");
    // depthVideoLabel->setMinimumSize(800, 600);  // 设置最小大小
    // depthVideoLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    // // depthVideoLabel->setFixedSize(760, 600);  // 设置显示区域大小为 800x600
    // mainLayout->addWidget(depthVideoLabel, 0, 1);  // 第0行，第1列

    // // 设置布局的行和列拉伸因子
    // mainLayout->setRowStretch(0, 1);  // 第0行拉伸
    // mainLayout->setColumnStretch(0, 1);  // 第0列拉伸
    // mainLayout->setColumnStretch(1, 1);  // 第1列拉伸

    // 第2行：方向盘和档位
    QGraphicsView *steeringView = new QGraphicsView(this);
    QGraphicsScene *steeringScene = new QGraphicsScene(this);
    steeringView->setMaximumHeight(300);  // 设置最大高度为 300
    steeringView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);  // 高度固定，宽度自适应
    steeringView->setScene(steeringScene);

    // 设置方向盘场景大小
    steeringScene->setSceneRect(-100, -100, 200, 200);

    // 创建方向盘控件并添加到场景中
    steeringWheel = new SteeringWheel;
    steeringScene->addItem(steeringWheel);

    // 将方向盘控件添加到主布局（第 1 行，第 0 列）
    mainLayout->addWidget(steeringView, 1, 0);

    // 创建档位按钮控件
    QGraphicsView *gearView = new QGraphicsView(this);
    QGraphicsScene *gearScene = new QGraphicsScene(this);
    gearView->setMaximumHeight(300);  // 设置最大高度为 300
    gearView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);  // 高度固定，宽度自适应
    gearView->setScene(gearScene);

    // 设置档位按钮场景大小
    gearScene->setSceneRect(-50, -100, 100, 200);

    // 创建档位按钮控件并添加到场景中
    m_gearControl = new GearControl;
    gearScene->addItem(m_gearControl);

    // 将档位按钮控件添加到主布局（第 1 行，第 1 列）
    mainLayout->addWidget(gearView, 1, 1);

    // 第3行：角度显示和命令显示
    m_angleLabel = new QLabel("当前角度: 0.0°", this);
    m_angleLabel->setAlignment(Qt::AlignCenter);
    m_angleLabel->setStyleSheet("font: 20pt; color: black;");
    mainLayout->addWidget(m_angleLabel, 2, 0);  // 第 2 行，第 0 列

    m_commandLabel = new QLabel("命令: 无", this);
    m_commandLabel->setAlignment(Qt::AlignCenter);
    m_commandLabel->setStyleSheet("font: 20pt; color: black;");
    mainLayout->addWidget(m_commandLabel, 2, 1);  // 第 2 行，第 1 列

    // 第4行：添加 QTextEdit 控件，用来显示runcmd的相关返回信息，但是会导致崩溃，应该是在槽函数修改主界面控件不被允许，所以改为只存日志文件了，不影响使用
//     QTextEdit *textOutput = new QTextEdit(this);

//     textOutput->setObjectName("textOutput");
//     textOutput->setReadOnly(true);
//     mainLayout->addWidget(textOutput, 3, 0, 1, 2);  // 第 3 行，第 0 列，占 1 行 2 列
// //    mainLayout->addWidget(textOutput);

//     // 连接信号槽，当文本发生变化时自动滚动到底部
//     connect(textOutput, &QTextEdit::textChanged, this, [textOutput]() {
//         textOutput->ensureCursorVisible();
//     });
//     textOutput->setText("init text test");
//     textOutput->append("time is");
//     const QString timestamp = QDateTime::currentDateTime().toString("[hh:mm:ss] ");
//     textOutput->append(timestamp);
//     // 当行数超过300行时，自动清除前面的内容
//     connect(textOutput, &QTextEdit::textChanged, this, [this]() {
// //        int lineCount = textOutput->document()->lineCount();
//         int lineCount = this->textOutput->document()->lineCount(); // 通过 this 访问成员变量
//         if (lineCount > 300) {
//             // this->saveLogToFile();  // 保存日志
//             QTextCursor cursor(this->textOutput->document());
//             cursor.movePosition(QTextCursor::Start);
//             cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, lineCount - 300);
//             cursor.removeSelectedText();
//         }
//     });

    //按钮容器，第4行
    QWidget *buttonContainer = new QWidget(this); // 创建一个容器部件
    QGridLayout *buttonLayout = new QGridLayout(buttonContainer); // 将网格布局设置给容器部件
    buttonManager = new ButtonManager(buttonLayout, this); // 将网格布局传递给 ButtonManager
    mainLayout->addWidget(buttonContainer,3,0,1,2); // 将容器部件添加到主布局中,第 4 行，第 0 列，占 1 行 2 列

    // 设置中心部件的布局
    centralWidget->setLayout(mainLayout);


    //初始化rclcpp，只能初始化一次，超过一次会运行编译报错
    int argc=0;
    char **argv=NULL;
    //侦听订阅采用rclcpp的API，node来创建节点
    rclcpp::init(argc,argv);
    //ROS2实例化
    rarrkey = new RobotArrawKey();

    // 连接信号槽
    connect(steeringWheel, &SteeringWheel::rotationChanged,
            this, &MainWindow::onRotationChanged);
    connect(m_gearControl, &GearControl::gearCommand,
            this, &MainWindow::onGearCommand);
    // 连接按钮点击信号到槽函数
    connect(buttonManager, &ButtonManager::buttonClicked, this, &MainWindow::handleButtonClick);

    //事件钩子初始化
    slam_hook = false;
    agent_hook = false;

}

void MainWindow::initViewIndicator()
{
    indicatorScene = new QGraphicsScene(this);
    ui->viewIndicator->setScene(indicatorScene);

    // 使用自定义的 GraphicsRectItem
    viewIndicator = new GraphicsRectItem;
    indicatorScene->addItem(viewIndicator);

    // 设置场景背景
    QLinearGradient gradient(0, 0, 0, 100);
    gradient.setColorAt(0, Qt::darkGray);
    gradient.setColorAt(1, Qt::black);
    indicatorScene->setBackgroundBrush(gradient);
}

void MainWindow::updateCameraFrame(const QVideoFrame &frame)
{
    QImage image = frame.image();
    if (!image.isNull()) {
        // 添加鱼眼效果
        QImage distorted(image.size(), QImage::Format_ARGB32);
        QPainter painter(&distorted);
        painter.setRenderHint(QPainter::Antialiasing);

        // 创建鱼眼变形
        for (int y = 0; y < image.height(); y++) {
            for (int x = 0; x < image.width(); x++) {
                float dx = (x - image.width()/2.0) / (image.width()/2.0);
                float dy = (y - image.height()/2.0) / (image.height()/2.0);
                float r = sqrt(dx*dx + dy*dy);
                float theta = 1.0 - r*r;

                int sx = image.width()/2 + dx*theta*image.width()/2;
                int sy = image.height()/2 + dy*theta*image.height()/2;

                if (sx >= 0 && sx < image.width() && sy >= 0 && sy < image.height()) {
                    distorted.setPixel(x, y, image.pixel(sx, sy));
                }
            }
        }
        ui->cameraView->setPixmap(QPixmap::fromImage(distorted));
    }
}

void MainWindow::rotateView(int degrees)
{
    QPropertyAnimation *animation = new QPropertyAnimation(viewIndicator, "rotation");
    animation->setDuration(500);
    animation->setStartValue(viewIndicator->rotation());
    animation->setEndValue(viewIndicator->rotation() + degrees);
    animation->setEasingCurve(QEasingCurve::InOutQuad);
    animation->start(QAbstractAnimation::DeleteWhenStopped);
}

void MainWindow::onRotationChanged(qreal angle)
{
    //在这里调整显示的角度，不会影响红十字的旋转
    qreal angle1 = 0;
    // 这里可以添加实际控制逻辑
    //变换转向角度值，将y轴正向的角度值变换为0度
    if(angle > 0)  angle1 = 180 - angle;
    else if(angle < 0){
        angle1 = -(180 + angle);
    }
    if(angle1 >= 180)   angle1 = 180;
    if(angle1 < -180)   angle1 = -180;
    m_angleLabel->setText(QString("当前角度: %1°").arg(angle1, 0, 'f', 1));

    if(angle1 > 0)  setanglearraw(KeyValue::left);
    else
        if(angle1 < 0) setanglearraw(KeyValue::right);
    else
        setanglearraw(KeyValue::none);
    qDebug() << "当前转向角度：" << angle;
    this->robotcontrol();
}

void MainWindow::onGearCommand(int command)
{
    QString cmd_text;
    switch (command) {
    case KeyValue::up:
        cmd_text = "前进";
        m_commandLabel->setText(cmd_text);
        setlinerarraw(KeyValue::up);
        break;
    case KeyValue::down:
        cmd_text = "后退";
        m_commandLabel->setText(cmd_text);
        setlinerarraw(KeyValue::down);
        break;
    case KeyValue::park:
        cmd_text = "驻车";
        m_commandLabel->setText(cmd_text);
        setlinerarraw(KeyValue::park);
        break;
    case KeyValue::stop:
        cmd_text = "手刹";
        m_commandLabel->setText(cmd_text);
        setlinerarraw(KeyValue::stop);
        break;
    default:
        break;
    }
    if(slam_hook)
        this->robotcontrol();
}

//调用运行外部命令的函数；
void MainWindow::ProcessCmd(QString str){
    // 限制最大并发数
    if(m_executors.count() > 10) {
        QMessageBox::warning(this, "警告", "同时运行的任务过多，请等待部分任务完成");
        return;
    }
    // 创建新的执行器实例
    runcmd* executor = new runcmd(this);
    m_executors.append(executor);

    // // 连接信号槽（使用lambda捕获发送者）
    connect(executor, &runcmd::outputReceived,this,
            [this, executor](const QString& output){
                handleOutput(output);
            });

    connect(executor, &runcmd::errorReceived,this,
            [this](const QString& error){
                handleError(error);
            });

    connect(executor, &runcmd::finished,this,
            [this, executor](int exitCode){
                handleFinished(exitCode, executor);
            });

    // 执行命令并清空输入框
    executor->runProcess(str);
}

void MainWindow::handleOutput(const QString& output)
{
    // 带时间戳的输出
    QString str = QString("<span style='color:red;'>%1 [OUTPUT] %2</span>")
                      .arg(QDateTime::currentDateTime().toString("[hh:mm:ss]"))
                      .arg(output);
    // qDebug() << str;
    // saveLogToFile(str);
}


void MainWindow::handleError(const QString& error)
{
    //显示错误状态
    QString str = QString("<span style='color:red;'>%1 [ERROR] %2</span>")
                      .arg(QDateTime::currentDateTime().toString("[hh:mm:ss]"))
                      .arg(error);
    qDebug() << str;
    saveLogToFile(str);
}

void MainWindow::handleFinished(int exitCode, runcmd* sender)
{
    // 显示退出状态
    QString str = QString("<span style='color:gray;'>%1 [退出] 状态码: %2</span>")
                      .arg(QDateTime::currentDateTime().toString("[hh:mm:ss]"))
                      .arg(exitCode);
    qDebug() << str;
    saveLogToFile(str);

    // 清理执行器对象
    m_executors.removeOne(sender);
    sender->deleteLater();
}

// 优雅停止所有进程
void MainWindow::gracefulStopAllProcesses()
{
    for (runcmd* executor : m_executors) {
        if (executor->isRunning()) {
            executor->stopProcess();
        }
    }
}

// 强制停止所有进程
void MainWindow::forceStopAllProcesses()
{
    for (runcmd* executor : m_executors) {
        if (executor->isRunning()) {
            executor->forceStop();
        }
    }
}

int MainWindow::getlinerarraw()
{
    return currentlinerarraw;
}

void MainWindow::setlinerarraw(KeyValue value)
{
    currentlinerarraw = value;
}

int MainWindow::getanglearraw()
{
    return currentanglearraw;
}

void MainWindow::setanglearraw(KeyValue value)
{
    currentanglearraw = value;
}

//实体机器人角速度、线速度控制，其实是固定值，并不是根据角度和速度来精准控制机器人运动
void MainWindow::robotcontrol()
{
    KeyValue liner = static_cast<KeyValue>(getlinerarraw());
    KeyValue angle = static_cast<KeyValue>(getanglearraw());
    switch(liner)
    {
    case up:
        if(angle == left){
            rarrkey->left_up();   //右转前行
            qDebug() << "left_up";
        }
            else if(angle == right){
                rarrkey->right_up();   //右转前行
            qDebug() << "right_up";
            }
                else {
                rarrkey->up();   //直行
                qDebug() << "up";
            }
        break;
    case down:
        if(angle == left){
            rarrkey->left_down();   //右转前行
            qDebug() << "left_down";
        }
        else if(angle == right){
            rarrkey->right_down();   //右转前行
            qDebug() << "right_down";
        }
        else {
            rarrkey->down();   //直行
            qDebug() << "down";
        }
        break;
    case park:
        if(angle == left){
            rarrkey->left();   //右转前行
            qDebug() << "left";
        }
        else if(angle == right){
            rarrkey->right();   //右转前行
            qDebug() << "right";
        }
        else {
            rarrkey->stop();   //直行
            qDebug() << "park";
        }
        break;
    case stop:
        rarrkey->stop();
        qDebug() << "stop";
        break;
    default:
        rarrkey->stop();
        qDebug() << "stop";
        break;
    }

}
// 保存日志到文件
void MainWindow::saveLogToFile(const QString &error) {
    QMutexLocker locker(&logMutex); // 加锁确保线程安全

    // 1. 检查并创建日志目录
    QString logDir = "log";
    QDir dir(logDir);
    if (!dir.exists()) {
        if (!dir.mkpath(".")) {
            qDebug() << "Failed to create log directory!";
            return;
        }
    }

    // 2. 生成日志文件名（含日期）
    QString currentDate = QDateTime::currentDateTime().toString("yyyy-MM-dd");
    QString logFileName = QString("%1/runcmd_%2.log").arg(logDir).arg(currentDate);

    // 3. 打开文件并追加写入（如果文件不存在则创建）
    QFile logFile(logFileName);
    if (!logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
        qDebug() << "Failed to open or create log file:" << logFileName;
        return;
    }

    // 4. 写入错误信息（含时间戳）
    QTextStream stream(&logFile);
    QString timestamp = QDateTime::currentDateTime().toString("[hh:mm:ss]");
    stream << timestamp << " [ERROR] " << error << "\n";

    //超过大小分新文件
    if (logFile.size() > 10 * 1024 * 1024) { // 10MB
        QString archivedName = QString("%1/runcmd_%2_%3.log")
                                   .arg(logDir)
                                   .arg(currentDate)
                                   .arg(QDateTime::currentDateTime().toString("hhmmss"));
        logFile.rename(archivedName);
        logFile.open(QIODevice::WriteOnly | QIODevice::Text); // 创建新文件
    }

    // 5. 关闭文件
    logFile.close();
}

//以后里面的命令行要能通过配置文件配置，比如ros2 run nav2_map_server map_saver_cli -t map -f /home/tf//physical_robot/src/two_wheels_robot/maps/tflivingroom_map
//里面的路径要能做到从配置文件里面取得，再组合成字串
void MainWindow::handleButtonClick(int button) {
    QStringList buttonNames = {"一键代理", "一键建图", "一键存图", "一键导航和避障","一键跟随", "关闭建图", "关闭导航和避障", "重启ROS2"};
    QString buttonName = "";
    QString str = "";
    switch(button)
    {
    case ButtonValue::agent:
        buttonName = "一键代理";
        //需要确保小车的激光雷达开启，否则ydlidar_launch.py超时后会自动退出，因此增加了小车是否开启的判断
        // 检查 /tmp/fishbot_laser 是否存在
        if (!QFile::exists("/tmp/fishbot_laser")) {
            // 弹出警告框
            QMessageBox msgBox;
            msgBox.setIcon(QMessageBox::Warning);
            msgBox.setWindowTitle("警告");
            msgBox.setText("请先启动机器人小车");
            msgBox.setStandardButtons(QMessageBox::Ok);

            // 显示警告框并等待用户点击“确定”
            if (msgBox.exec() == QMessageBox::Ok) {
                // 退出函数
                // return;
            }
        }

        // 如果文件存在，继续执行其他逻辑
        qDebug() << "机器人小车已启动，继续执行其他操作...";
//使用一个launch做完上面所有动作，感觉问题出在时间上，虚拟串口还没建立，ydlidar_launch就执行了，这样还能让runcmd类只处理命令行
        str = "ros2 launch two_wheels_robot agent_rviz2_open.launch.py";
        this->ProcessCmd(str);
        agent_hook = true;
        break;
    case ButtonValue::slam: //如果是无线的话，必须要一键代理
        buttonName = "一键建图";
        if(agent_hook == false)
        {
            // 弹出警告框
            QMessageBox msgBox;
            msgBox.setIcon(QMessageBox::Warning);
            msgBox.setWindowTitle("警告");
            msgBox.setText("请先一键代理");
            msgBox.setStandardButtons(QMessageBox::Ok);

            // 显示警告框并等待用户点击“确定”
            if (msgBox.exec() == QMessageBox::Ok) {
                // 退出函数
                return;
            }
        }
        //运行TF转换Launch-Bringup，确定运行执行后再执行slam
        str = "ros2 launch fishbot_bringup fishbot_bringup.launch.py&&ros2 launch slam_toolbox online_async_launch.py";
        this->ProcessCmd(str);
        slam_hook = true;
        break;
    case ButtonValue::map:
        buttonName = "一键存图";
        str = "ros2 run nav2_map_server map_saver_cli -t map -f /home/tf/physical_robot/src/two_wheels_robot/maps/tflivingroom_map";
        this->ProcessCmd(str);
        break;
    case ButtonValue::nav:
        buttonName = "一键导航和避障";
        str = "ros2 launch fishbot_navigation2 navigation2.launch.py";
        this->ProcessCmd(str);
        break;
    case ButtonValue::follow:
        buttonName = "一键跟随";
        break;
    case ButtonValue::closeslam:
        buttonName = "关闭建图";
        slam_hook = false;
        agent_hook = false;
        break;
    case ButtonValue::closenav:
        buttonName = "关闭导航";
        break;
    case ButtonValue::resetros2:
        buttonName = "重启ROS2";
        gracefulStopAllProcesses();
        break;
    default:
        buttonName = "乱麻";
        break;
    }
    qDebug() << "按钮被点击：" << buttonName; // 打印按钮名称
}

// Socket 连接成功
void MainWindow::onSocketConnected()
{
    qDebug() << "成功连接到服务器";
}

// Socket 断开连接
void MainWindow::onSocketDisconnected()
{
    qDebug() << "与服务器断开连接";
}

// Socket 发生错误
void MainWindow::onSocketError(QAbstractSocket::SocketError error)
{
    qDebug() << "Socket 错误:" << socket->errorString();
}

// 处理接收到的数据
void MainWindow::onSocketReadyRead()
{
    buffer += socket->readAll();

    while (true) {
        if (frameSize == 0 && buffer.size() >= sizeof(quint64)) {
            QDataStream stream(&buffer, QIODevice::ReadOnly);
            stream.setByteOrder(QDataStream::LittleEndian);
            stream >> frameSize;
            buffer.remove(0, sizeof(quint64));
            // qDebug() << "读取帧大小:" << frameSize;
        }

        if (frameSize > 0 && buffer.size() >= frameSize) {
            QByteArray frameData = buffer.left(frameSize);
            buffer.remove(0, frameSize);
            frameSize = 0;

            // 解码 JPEG 图像
            QImage frame;
            if (frame.loadFromData(frameData, "JPEG")) {
                // qDebug() << "解码成功，图像大小:" << frame.size();
                updateFrame(frame);
            } else {
                qDebug() << "解码失败";
            }
        } else {
            break;
        }
    }
}

// 更新显示帧（主线程）
void MainWindow::updateFrame(const QImage &frame)
{
    // 帧率控制：30 FPS
    if (isFirstFrame) {
        frameTimer.start();
        isFirstFrame = false;
    } else if (frameTimer.elapsed() < 33) {
        return;
    }
    frameTimer.restart();

    // 显示图像
    QPixmap pixmap = QPixmap::fromImage(frame);
    if (pixmap.isNull()) {
        qDebug() << "QPixmap 创建失败";
        return;
    }
    pixmap = pixmap.scaled(colorVideoLabel->size(), Qt::KeepAspectRatio);
    colorVideoLabel->setPixmap(pixmap);
}

MainWindow::~MainWindow()
{
    delete ui;
    // if (camera) {
    //     camera->stop();
    //     delete camera;
    // }

    // 断开连接并停止线程
    if (socket) {
        socket->disconnectFromHost();
        socket->deleteLater();
    }
    forceStopAllProcesses();
    // 清理所有qprocess执行器
    qDeleteAll(m_executors);      // 强制释放所有执行器
}
